/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: executor.cpp
 *
 * Purpose: task executor implementation.
 *
 * Developer:
 *   wen.gu , 2021-04-02
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "icpp/executor/executor.h"

#include <vector>
#include <mutex>
#include <condition_variable>

#include "icpp/core/types.h"
#include "icpp/executor/thread.h"

namespace icpp
{
namespace executor
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

class Executor::Impl
{
public:
    using AutoLock = icpp::core::AutoLock;
    using TaskQueue = std::vector<Executor::Task>;
public: 
    /** 
     * if task_queue_size == 0, then unlimit the size of normal task queue 
     * if urgent_task_queue_size == 0, then unlimit the size of urgent task queue 
     */
    Impl(uint32_t task_queue_size, uint32_t urgent_task_queue_size);

public:
    bool is_running_ = false; 
    uint32_t max_normal_size_;
    uint32_t max_urgent_size_;
    Thread* thd_ = nullptr;
    TaskQueue normal_tasks_;
    TaskQueue urgent_tasks_;


    std::mutex  lock_;
    std::condition_variable cond_;
    std::string name_;

public:
    void executeUrgentTask();

};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
Executor::Impl::Impl(uint32_t task_queue_size, uint32_t urgent_task_queue_size)
    :max_normal_size_(task_queue_size),
    max_urgent_size_(urgent_task_queue_size)
{
    /** todo something */
}

void Executor::Impl::executeUrgentTask()
{
    TaskQueue tks;
    {
        AutoLock al(lock_);
        tks.swap(urgent_tasks_);
    }
    for (uint32_t i = 0; (i < tks.size()) && is_running_; i++)
    {
        tks[i]();
    }
    
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

 /**
  * the queue size if set as 0, then indicat that there is no limit for the size of queue.
  */
Executor::Executor(const std::string& name, uint32_t task_queue_size /*= 0*/, uint32_t urgent_task_queue_size /*= 0*/)
    :impl_(new Impl(task_queue_size, urgent_task_queue_size))
{
    /** todo something */
    impl_->name_ = name;
}

Executor::~Executor()
{
    /** todo something */
    stop();
}

bool Executor::start()
{
    Impl::AutoLock al(impl_->lock_);
    if (!impl_->is_running_)
    {
        impl_->is_running_ = true;
        impl_->thd_ = new Thread(impl_->name_, [this]() -> bool
        {
            {
                Impl::AutoLock al(this->impl_->lock_);
                this->impl_->cond_.wait(al, [this] {
                    return this->impl_->is_running_ || !this->impl_->normal_tasks_.empty() ||
                        !this->impl_->urgent_tasks_.empty();
                    }
                ); // wait until have a task in queue.  

                if (this->impl_->is_running_ /*&& this->impl_->normal_tasks_.empty() && 
                    this->impl_->urgent_tasks_.empty()*/) /**todo, refine me?? */
                {
                    return false;
                }
            }

            this->impl_->executeUrgentTask();
            Impl::TaskQueue normal_tasks;
            {
                Impl::AutoLock al(this->impl_->lock_);
                normal_tasks.swap(this->impl_->normal_tasks_);
            }

            for (size_t i = 0; (i < normal_tasks.size()) && this->impl_->is_running_; i++)
            {
                this->impl_->executeUrgentTask();
                normal_tasks[i]();
            }

            return true;
        });

        return true;

    }

    return false;
}

bool Executor::stop()
{
    if (impl_->thd_)
    {
        {
            Impl::AutoLock al(impl_->lock_);
            impl_->is_running_ = false;
            impl_->cond_.notify_all();
        }
        
        impl_->thd_->join();
        delete impl_->thd_;
        impl_->thd_ = nullptr;
        return true;
    }
    return false;
}

bool Executor::set_priority(int32_t priority)
{
    if (impl_->thd_)
    {
        return impl_->thd_->set_priority(priority);
    }

    return false;
}

bool Executor::set_name(const std::string& name)
{
    if (impl_->thd_)
    {
        return impl_->thd_->set_name(name);
    }

    return false;
}

int32_t Executor::priority() const
{
    if (impl_->thd_)
    {
        return impl_->thd_->priority();
    }

    return 0;
}

const std::string& Executor::name() const
{
    if (impl_->thd_)
    {
        return impl_->thd_->name();
    }

    return "";
}

/**
 * @brief add task to executor
 *
 * @urgent true: add task to urgent task queue, false add task to normal task queue
 *
 * @note The executor have 2 task queue, urgent and normal. The executor aways query urgent
 *       queue at first, normal queue at second.
 */
bool Executor::submitTask(Task&& task, bool urgent /*= false*/)
{
    Impl::AutoLock al(impl_->lock_);
    if (urgent)
    {
        if (!impl_->max_urgent_size_ || (impl_->urgent_tasks_.size() < impl_->max_urgent_size_))
        {
           impl_->urgent_tasks_.push_back(std::move(task));
        }
        else
        {
            return false;
        }
        
        
    }
    else
    {
        if (!impl_->max_normal_size_ || (impl_->normal_tasks_.size() < impl_->max_normal_size_))
        {
            impl_->normal_tasks_.push_back(std::move(task));
        }
        else
        {
            return false;
        }
    }

    uint32_t urgent_size = impl_->urgent_tasks_.size();
    uint32_t normal_size = impl_->normal_tasks_.size();

    if (((normal_size == 1) && (urgent_size == 0)) || ((urgent_size == 1) && (normal_size == 0)))
    {
        impl_->cond_.notify_all();
    }

    return true;
}

} /** namespace executor */
} /** namespace icpp */


